// <copyright file="SSISWrapper.cs" company="Bill Fellows">
// Copyright (c) But not really, use it as you see fit. All rights reserved.
// </copyright>
namespace SSISDeploy.Components
{
    using System;
    using System.Collections.Generic;
    using System.Text;

    using Microsoft.SqlServer.Dts.Runtime;

    public class SSISWrapper
    {
        /// <summary>
        /// An instance of an application.  Provides most of the file operations
        /// </summary>
        private Application application;

        /// <summary>
        /// Default constructor
        /// </summary>
        public SSISWrapper()
        {
            application = new Application();
        }

        /// <summary>
        /// Create a folder on SQL server.
        /// TODO:  Globally, figure out what will be the most appropriate way of handling
        /// and reporting errors.
        /// </summary>
        /// <param name="server">A SQL Server name</param>
        /// <param name="parentFolderPath">The path to the parent folder.  Default is \MSDB</param>
        /// <param name="newFolderName">The name of the folder to be created</param>
        /// <returns>true/false based on the successful creation of a folder</returns>
        public bool CreateFolder(string server, string parentFolderPath, string newFolderName)
        {
            bool folderCreated = false;
            Console.WriteLine(string.Format("Creating folder {0} as a child of {1} on server {2}", newFolderName, parentFolderPath, server));
            
            // I am assuming they've at least started with \folder
            // What about root?
            parentFolderPath = this.ManipulateRootFolder(parentFolderPath);

            Console.WriteLine(string.Format("Creating folder {0} as a child of {1} on server {2}", newFolderName, parentFolderPath, server));

            try
            {
                application.CreateFolderOnDtsServer(parentFolderPath, newFolderName, server);
                folderCreated = true;
            }
            catch (Microsoft.SqlServer.Dts.Runtime.DtsPipelineException exception)
            {
                // probably something like duplicate folder
                // Not sure if we care at this point beyond saying No
                folderCreated = false;
                Console.WriteLine(exception.ToString());
            }
            catch (System.ArgumentException exception)
            {
                // Most likely the result of a bad path
                folderCreated = false;
                Console.WriteLine(exception.ToString());
            }
            catch (Exception exception)
            {
                Console.WriteLine(exception.ToString());
            }

            return folderCreated;
        }

        /// <summary>
        /// Create all the folders given a path
        /// </summary>
        /// <param name="server">A SQL Server servername</param>
        /// <param name="packagePath">A fully qualified SQL Server path</param>
        /// <returns>true if it was able to create all the folders</returns>
        public bool CreateFoldersRecursive(string server, string packagePath)
        {
            bool results = false;
            string parentFolder = string.Empty;
            string currentFolder = string.Empty;
            packagePath = this.ManipulateRootFolder(packagePath);
            parentFolder = this.GetParentFolder(packagePath);
            currentFolder = this.GetCurrentFolder(packagePath);
            if (!this.FolderExists(server, packagePath))
            {
                this.CreateFoldersRecursive(server, parentFolder);
                results = this.CreateFolder(server, parentFolder, currentFolder);
            }
            else
            {
                results = true;
            }

            return results;
        }

        /// <summary>
        /// Delete a folder on SQL Server
        /// </summary>
        /// <param name="server">A SQL Server server</param>
        /// <param name="parentFolderPath">The path to the parent folder</param>
        /// <param name="folderName">The name of the folder to be removed</param>
        /// <returns>true/false based on whether we were able to purge the folder</returns>
        public bool DeleteFolder(string server, string parentFolderPath, string folderName)
        {
            bool folderDeleted = false;
            Console.WriteLine(string.Format("Deleting {0} from {1} on {2}", folderName, parentFolderPath, server));
            parentFolderPath = this.ManipulateRootFolder(parentFolderPath);
            Console.WriteLine(string.Format("Deleting {0} from {1} on {2}", folderName, parentFolderPath, server));
            try
            {
                application.RemoveFolderFromDtsServer(string.Format(@"{0}{1}", parentFolderPath, folderName), server);
                folderDeleted = true;
            }
            catch (Exception exception)
            {
                Console.WriteLine(exception.ToString());
            }

            return folderDeleted;
        }

        /// <summary>
        /// Remove all the elements and sub-elements from a SQL Server SSIS deployment
        /// </summary>
        /// <param name="server">A SQL Server servername</param>
        /// <param name="parentFolderPath">The root location to begin pruning</param>
        /// <returns>true/false based on the successful removal of content</returns>
        public bool DeleteFolderRecursive(string server, string parentFolderPath)
        {
            bool folderDeleted = false;
            string parameters = string.Empty;
            parentFolderPath = this.ManipulateRootFolder(parentFolderPath);
            Console.WriteLine(string.Format("Calling DeleteFolderRecursive on {0}", parentFolderPath));
            foreach (PackageFolder item in this.GetDirectory(server, parentFolderPath))
            {
                // Console.WriteLine(item);
                if (item.IsFolder)
                {
                    this.DeleteFolderRecursive(server, string.Format(@"{0}{1}", parentFolderPath, item.Name));
                    folderDeleted = this.DeleteFolder(server, parentFolderPath, item.Name);
                }
                else if (item.IsPackage)
                {
                    // folderDeleted = this.DeletePackage(server, parentFolderPath, item.Name);
                }
            }

            return folderDeleted;
        }
/*
        /// <summary>
        /// Remove a DTSX/SSIS package from SQL server
        /// </summary>
        /// <param name="server">A SQL Server servername</param>
        /// <param name="parentFolderPath">The root location to find a package</param>
        /// <param name="packageName">The name of the SSIS package as it is defined in SQL Server (sans-extension)</param>
        /// <returns>true/false based on the successful removal of a package</returns>
        public bool DeletePackage(string server, string parentFolderPath, string packageName)
        {
            bool packageDeleted = false;
            string parameters = string.Empty;
            parameters = string.Format(@" /SourceS {0} /Delete /SQL ""{1}\\{2}""", server, parentFolderPath, packageName);

            // Console.WriteLine(parameters);
            packageDeleted = this.RunProcess(parameters);
            return packageDeleted;
        }

        /// <summary>
        /// Transfer an SSIS package to the SQL Server
        /// </summary>
        /// <param name="server">A SQL Server servername</param>
        /// <param name="pathToDtsx">The location on disk (local or UNC) where the file may be found</param>
        /// <param name="packagePathAndName">The fully qualified location of where the package should reside in SQL Server as well as its name</param>
        /// <returns>true/false based on the successful copy of a package</returns>
        public bool CopyPackage(string server, string pathToDtsx, string packagePathAndName)
        {
            bool packageLoaded = false;
            string parameters = string.Empty;
            parameters = string.Format(@" /DestS {0} /File ""{1}"" /Copy ""SQL;{2}""", server, pathToDtsx, packagePathAndName);

            // Console.WriteLine(parameters);
            packageLoaded = this.RunProcess(parameters);
            return packageLoaded;
        }

        /// <summary>
        /// Test whether an item exists in SQL Server
        /// </summary>
        /// <param name="server">A SQL Server servername</param>
        /// <param name="packagePathAndName">The fully qualified path and packagename in SQL Server</param>
        /// <returns>true/false based on whether the item exists within SQL Server</returns>
        public bool PackageExists(string server, string packagePathAndName)
        {
            bool packageExists = false;
            string parameters = string.Empty;
            parameters = string.Format(@" /SourceS {0} /Exists /SQL ""{1}""", server, packagePathAndName);

            // Console.WriteLine(parameters);
            packageExists = this.RunProcess(parameters);
            return packageExists;
        }
*/
        /// <summary>
        /// Test whether a folder exists in SQL Server
        /// </summary>
        /// <param name="server">A SQL Server servername</param>
        /// <param name="packagePath">The fully qualified path in SQL Server</param>
        /// <returns>true/false based on whether the folder exists within SQL Server</returns>
        public bool FolderExists(string server, string packagePath)
        {
            bool packageExists = false;
            packagePath = this.ManipulateRootFolder(packagePath);
            packageExists = application.FolderExistsOnDtsServer(packagePath, server);
            return packageExists;
        }

        /// <summary>
        /// Test whether a folder exists in SQL Server
        /// </summary>
        /// <param name="server">A SQL Server servername</param>
        /// <param name="parentFolderPath">The path to a folder's parent</param>
        /// <param name="folderName">The name of folder</param>
        /// <returns>true/false based on whether the folder exists</returns>
        public bool FolderExists(string server, string parentFolderPath, string folderName)
        {
            // Should I push this into the invoking method's logic?
            if (!(parentFolderPath.EndsWith(@"\\") || folderName.StartsWith(@"\\")))
            {
                parentFolderPath = parentFolderPath + @"\\";
            }

            // a path should look like \something\some\something
            return this.FolderExists(server, parentFolderPath + folderName);
        }
/*
        /// <summary>
        /// Rename a folder
        /// </summary>
        /// <param name="server">The server to attempt the rename operation against</param>
        /// <param name="parentPath">The fully qualified path to the parent folder</param>
        /// <param name="oldFolder">The item to be renamed</param>
        /// <param name="newFolder">The new name for the item</param>
        /// <returns>A boolean indicating success/failure</returns>
        public bool RenameFolder(string server, string parentPath, string oldFolder, string newFolder)
        {
            bool rename = false;
            string parameters = string.Empty;
            parameters = string.Format(@" /SourceS {0} /FRename ""SQL;{1};{2};{3}", server, parentPath, oldFolder, newFolder);

            this.RunProcessWithOutput(parameters);
            rename = this.results.ReturnCode == 0;
            return rename;
        }
*/
        /// <summary>
        /// Retrieve the contents of a folder within SQL Server
        /// </summary>
        /// <param name="server">A SQL Server servername</param>
        /// <param name="parentFolderPath">The fully qualified path in SQL Server.  e.g. \ or \folder1\folder2\folder3</param>
        /// <returns>A list of PackageFolder objects containing the contents of parentFolderPath</returns>
        public List<PackageFolder> GetDirectory(string server, string parentFolderPath)
        {
            List<PackageFolder> contents = null;
            PackageFolder packageFolder = null;
            contents = new List<PackageFolder>();

            Microsoft.SqlServer.Dts.Runtime.PackageInfos pi = null;
            pi = this.application.GetDtsServerPackageInfos(parentFolderPath, server);
            // foreach (PackageInfo info in this.application.GetDtsServerPackageInfos("\\msdb", server))
            Console.WriteLine(string.Format("Getting information for {0}", parentFolderPath));
            foreach (PackageInfo info in pi)
            {
                packageFolder = new PackageFolder();
                packageFolder.IsPackage = info.Flags == DTSPackageInfoFlags.Package;
                packageFolder.IsFolder = info.Flags == DTSPackageInfoFlags.Folder;

                if (packageFolder.IsPackage)
                {
                    packageFolder.CreateDate = info.CreationDate.ToString();
                    packageFolder.Path = info.Folder;
                    packageFolder.Version = string.Format("v{0}.{1}.{2}", info.VersionBuild, info.VersionMajor, info.VersionMinor);
                }
                packageFolder.Name = info.Name;
                Console.WriteLine(packageFolder);
                contents.Add(packageFolder);
            }

            return contents;
        }

        /// <summary>
        /// Doctor up the input path so that people using \Folder don't have to do tricks of 
        /// \MSDB\Folder depending on the engine
        /// </summary>
        /// <param name="path">A path in SQL Server</param>
        /// <returns>Make sure it has a valid root location</returns>
        private string ManipulateRootFolder(string path)
        {
            string outPath = string.Empty;
            string root = string.Empty;
            string seperator = string.Empty;
            root = @"\MSDB";
            seperator = "\\";

            if (path.Equals(seperator + seperator))
            {
                path = seperator;
            }

            if (!path.StartsWith(seperator))
            {
                path = seperator + path;
            }

            // If it doesn't start with \MSDB, make it so
            if (!path.StartsWith(root, StringComparison.CurrentCultureIgnoreCase))
            {
                outPath = root;
            }

            // Make sure it ends with a slash?
            if (!path.EndsWith("\\"))
            {
                path += "\\";
            }

            outPath += path;
            return outPath;
        }

        /// <summary>
        /// Get the last folder listed
        /// </summary>
        /// <param name="packagePath">A fully qualifed SQL Server path</param>
        /// <returns>The current folder</returns>
        private string GetCurrentFolder(string packagePath)
        {
            string currentFolder = string.Empty;
            int lastSeparator = 0;
            lastSeparator = packagePath.LastIndexOf("\\") + 1;
            currentFolder = packagePath.Substring(lastSeparator, packagePath.Length - lastSeparator);

            return currentFolder;
        }

        /// <summary>
        /// Given a path, return the enclosing folder.
        /// </summary>
        /// <param name="packagePath">A fully qualified SQL Server path</param>
        /// <returns>A fully qualified SQL Server path one level above what was sourced</returns>
        private string GetParentFolder(string packagePath)
        {
            string parentPath = string.Empty;
            string rootPath = string.Empty;
            System.IO.DirectoryInfo parentDirectory = null;
            parentPath = "\\";
            rootPath = @"\\msdb";
            Console.WriteLine(string.Format("ppath {0}", packagePath));

            try
            {
                if (packagePath != rootPath)
                {
                    parentDirectory = System.IO.Directory.GetParent(packagePath);

                    if (parentDirectory != null)
                    {
                        parentPath = parentDirectory.ToString().Replace(System.IO.Directory.GetDirectoryRoot(parentDirectory.ToString()), parentPath);
                    }

                    if (parentPath == "\\")
                    {
                        parentPath = rootPath;
                    }
                }
            }
            catch (ArgumentException exception)
            {
                // chose to do nothing with it.  Shouldn't be an issue as we 
                // test above, but UNCs have not been tested
            }

            return parentPath;
        }
    }
}
